//
//  TCUXMLTree.m
//  TreeMenuTest
//
//  Created by Etienne on 12/07/07.
//  Copyright (c) 2007 Etienne Samson. All rights reserved.
//

#import "TCUXMLNode.h"
#import "TCUXMLParser.h"

#import "TCUXMLTree.h"

@class TCUCFTree, TCUCFTree__;

#include "CFRuntime_internal.h"
NSString* const NSXMLTreeErrorDomain = @"NSXMLTreeErrorDomain";

@implementation TCUXMLTree

+ (void) load
{
    _CFRuntimeInitializeClassForBridging(CFTreeGetTypeID());
    if (_CFRuntimeSetupBridging(CFTreeGetTypeID(), [TCUCFTree class], [TCUCFTree__ class]))
        NSLog(@"Successfully setup bridge for TCUCFTree...");
    else
        NSLog(@"Failed _CFRuntimeSetupBridging()");
}

+ (id) xmlTreeWithNode:(TCUXMLNode*)node
{
    if (![node isKindOfClass:[TCUXMLNode class]])
        [NSException raise:NSInvalidArgumentException format:@"Can't create XML tree with non-TCUXMLNode object"];
    
    return [[[TCUXMLTree alloc] initWithNode:node] autorelease];
}

+ (id) xmlTreeWithURL:(NSURL*)url options:(TCUXMLParserOption)options
{
    return [[[TCUXMLTree alloc] initWithURL:url parserOptions:options] autorelease];
}

+ (id) xmlTreeWithData:(NSData*)data options:(TCUXMLParserOption)options
{
    return [[[TCUXMLTree alloc] initWithData:data
                                     parserOptions:options] autorelease];
}


/*" Generate an tree for a specific standalone XML document, ready built with its xml declaration added.
"*/
+ (id) treeForStandaloneXMLDocumentWithURL: (NSURL*) url
                                  encoding: (NSStringEncoding) encoding
                                   version: (float) xmlVersion
{
    CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSString stringWithFormat:@"%.1f", xmlVersion],        @"version",
        CFStringConvertEncodingToIANACharSetName(cfEncoding),   @"encoding",
        @"yes",                                                 @"standalone",
        nil];
    
    TCUXMLNode *documentNode = [TCUXMLNode documentNodeWithURL:url encoding:encoding];        
    TCUXMLNode *xmlNode = [TCUXMLNode processingInstructionNodeWithName:@"xml" attributes:dict];
    
    TCUXMLTree *tree = [TCUXMLTree xmlTreeWithNode:documentNode];
    [tree addXMLChild:[TCUXMLTree xmlTreeWithNode:xmlNode]];
    return tree;
}

/*" Generate an tree for a specific XML document, ready built with its xml and DTD declarations added.
"*/
+ (id) treeForXMLDocumentWithURL: (NSURL*) url
                        encoding: (NSStringEncoding) encoding
                         version: (float) xmlVersion
                          dtdURL: (NSURL*) dtdUrl
                         dtdRoot: (NSString*) dtdRoot
                   dtdPublicName: (NSString*) dtdPublicName
{
    CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
    
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSString stringWithFormat:@"%.1f", xmlVersion],        @"version",
        CFStringConvertEncodingToIANACharSetName(cfEncoding),   @"encoding",
        @"no",                                                  @"standalone",
        nil];
    
    TCUXMLNode *documentNode = [TCUXMLNode documentNodeWithURL:url encoding:encoding];
    TCUXMLNode *xmlNode = [TCUXMLNode processingInstructionNodeWithName:@"xml" attributes:dict];
    TCUXMLNode *dtdNode = [TCUXMLNode documentTypeNodeWithURL:dtdUrl forRootElement:dtdRoot publicName:dtdPublicName];
    
    TCUXMLTree *tree = [TCUXMLTree xmlTreeWithNode:documentNode];
    [tree addXMLChild:[TCUXMLTree xmlTreeWithNode:xmlNode]];
    [tree addXMLChild:[TCUXMLTree xmlTreeWithNode:dtdNode]];
    return tree;
}

- (id) initWithNode:(TCUXMLNode*)node
{
    return (TCUXMLTree*)CFXMLTreeCreateWithNode(NULL, (CFXMLNodeRef)node);
}

- (id) initWithURL:(NSURL*)url
{
    return [self initWithURL:url parserOptions:TCUXMLParserNoOptions];
}

- (id) initWithURL:(NSURL*)url parserOptions:(TCUXMLParserOption)options
{
    return (TCUXMLTree*)CFXMLTreeCreateWithDataFromURL(NULL, (CFURLRef)url, options, kCFXMLNodeCurrentVersion);
}

- (id) initWithData:(NSData*)data
{
    return [self initWithData:data parserOptions:TCUXMLParserNoOptions];
}

- (id) initWithData:(NSData*)data parserOptions:(TCUXMLParserOption)options
{
    return (TCUXMLTree*)CFXMLTreeCreateFromData(NULL,(CFDataRef)data, NULL, options, kCFXMLNodeCurrentVersion);
}

- (id) initWithData:(NSData*)data error:(NSError**)error
{
    NSDictionary **dict = nil;
    self = (TCUXMLTree*)CFXMLTreeCreateFromDataWithError(NULL,(CFDataRef)data, NULL, 0, kCFXMLNodeCurrentVersion, (CFDictionaryRef*)dict);
    
    if (dict != nil) {
        *error = [NSError errorWithDomain:NSXMLTreeErrorDomain
                                      code:1
                                  userInfo:*dict];
    }
    
    return self;
}

/*"Return the contents of this tree as XML Data.

%{The tree should contain references to only TCUXMLNode objects or behaviour is undefined}"*/
- (NSData*)xmlData
{
    return (NSData*)CFXMLTreeCreateXMLData(NULL,(CFXMLTreeRef)self);
}

- (TCUXMLNode*)node
{
    return (TCUXMLNode*)CFXMLTreeGetNode((CFXMLTreeRef)self);
}

/*"Add a child tree referencing an XML node to this tree.

%{Raises NSInvalidArgumentException if either this or the child tree does not reference an XML object.}"*/
- (void) addXMLChild: (TCUXMLTree*) tree {
    [self addXMLChild:tree atIndex:[self childCount]];
}

/*"Add a child tree referencing an XML node to this tree, at the given index.

%{Raises NSInvalidArgumentException if either this or the child tree does not reference an XML object.}"*/
- (void) addXMLChild: (TCUXMLTree*) tree
             atIndex: (unsigned) index
{
    if (![[tree object] isKindOfClass:[TCUXMLNode class]])
        [NSException raise:NSInvalidArgumentException format:@"Object %@ does not reference an XML node", self];

#warning FIXME CFXMLElementInfo isEmpty flag must be tweaked...
    [self addChild:tree atIndex:index];
}

/*"Return an array of subtrees referencing XML nodes with the given type.

%{As usual, this method does not act recursively.}"*/
- (NSArray*) childTreesReferencingXMLNodesOfType: (TCUXMLNodeType) type {
    NSMutableArray * array = [NSMutableArray array];
    NSEnumerator * enumerator = [self childEnumerator];
    id child;
    
    while ((child = [enumerator nextObject])) {
        if ([[child object] isKindOfClass:[TCUXMLNode class]]) {
            if ([[child object] nodeType] == type) {
                [array addObject:child];
            }
        }
    }
    return [[array copy] autorelease];
}
@end
